﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;

using NHibernate;
using NHibernate.Linq;

namespace ZQNB.Entity
{
	public class Repository< T > : IRepository<T>
		where T : IAggregateRoot
	{
		private readonly ISessionManagementStrategy sessionManagementStrategy;

		public Repository ( ISessionManagementStrategy sessionManagementStrategy )
		{
			this.sessionManagementStrategy = sessionManagementStrategy;
		}

		protected ISessionManagementStrategy SessionManager
		{
			get { return sessionManagementStrategy; }
		}

		#region Implementation of IQueryable

		public Expression Expression
		{
			get { return SessionManager.GetSession ().Query<T> ().Expression; }
		}

		public Type ElementType
		{
			get { return SessionManager.GetSession ().Query<T> ().ElementType; }
		}

		public IQueryProvider Provider
		{
			get { return SessionManager.GetSession ().Query<T> ().Provider; }
		}

		#endregion

		#region Implementation of ICollection

		/// <summary>
		/// </summary>
		/// <param name = "item"></param>
		/// <exception cref = "ArgumentNullException">Thrown if item is null</exception>
		public void Add ( T item )
		{
			if ( !SessionManager.GetSession ().Query<T> ().Contains ( item ) )
			{
				SessionManager.GetSession ().Save ( item );

				if ( this.InstanceAdded != null )
				{
					this.InstanceAdded ( this , new InstanceAddedEventArgs<T> ( item ) );
				}
			}
		}

		public void Clear ()
		{
			throw new NotSupportedException ();
		}

		public bool Contains ( T item )
		{
			return SessionManager.GetSession ().Query<T> ().Contains ( item );
		}

		public void CopyTo ( T[] array , int arrayIndex )
		{
			throw new NotSupportedException ();
		}

		public int Count
		{
			get { return SessionManager.GetSession ().Query<T> ().Count (); }
		}

		public bool IsReadOnly
		{
			get { return false; }
		}

		public bool Remove ( T item )
		{
			SessionManager.GetSession ().Delete ( item );
			return true;
		}

		public IEnumerator<T> GetEnumerator ()
		{
			return SessionManager.GetSession ().Query<T> ().AsEnumerable ().GetEnumerator ();
		}

		IEnumerator IEnumerable.GetEnumerator ()
		{
			return this.GetEnumerator ();
		}

		#endregion

		#region Implementation of IRepository

		public IQueryable<T> Include ( Expression<Func<T , object>> subSelector )
		{
			return SessionManager.GetSession ().Query<T> ().Fetch ( subSelector );
		}

		public void Update ( T item )
		{
			SessionManager.GetSession ().Save ( item );
		}

		#endregion

		/// <summary>
		/// 	Raised when a customer is placed into the repository.
		/// </summary>
		public event EventHandler<InstanceAddedEventArgs<T>> InstanceAdded;
	}

    public interface ISessionManagementStrategy
    {
        ISession GetSession();
    }

    public class InstanceAddedEventArgs<T> : EventArgs
    {
        public InstanceAddedEventArgs(T newInstance)
        {
            this.NewInstance = newInstance;
        }

        public T NewInstance { get; private set; }
    }
}